#include<arpa/inet.h>
#include<netinet/in.h>
#include<ctype.h>
#include"wrap.h"
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/socket.h>

/*
   实现多进程的网络通信
 */


void waitchild(int signo)
{
	pid_t wpid;
	while(1)
	{
		wpid = waitpid(-1,NULL,WNOHANG);
		if(wpid>0)
		{
			printf("child exit,wpid==[%d]\n",wpid);
		}
		else if(wpid==0 || wpid==-1)
		{
			break;
		}
	}


}




int main()
{
	//1、创建socket
	int lfd = Socket(AF_INET,SOCK_STREAM,0);	

	//2、绑定
	struct sockaddr_in serv;
	bzero(&serv,sizeof(serv));//初始化
	serv.sin_family = AF_INET;
	serv.sin_port = htons(6666);
	serv.sin_addr.s_addr = htonl(INADDR_ANY);
	Bind(lfd,(struct sockaddr*)&serv,sizeof(serv));
	//3、设置监听
	Listen(lfd,128);


	//阻塞SIGCHLG信号,防止信号注册过程没有完成，子进程已经结束
	sigset_t mask;//阻塞信号集
	sigemptyset(&mask);//将信号集清空
	sigaddset(&mask,SIGCHLD);//将SIGCHLD信号加入阻塞信号集中
	//阻塞
	sigprocmask(SIG_BLOCK<&mask,NULL);



	//进入循环，获得连接
	int cfd;
	pid_t pid;
	char sIP[16];
	socklen_t len;
	struct sockaddr_in client;
	while(1)
	{
		len=sizeof(client);
		//接受新的链接
		cfd = Accept(lfd,(struct sockaddr*)&client,&len);
		printf("client:[%s],[%d]\n",inet_ntop(AF_INET,&client.sin_addr.s_addr,sIP,sizeof(sIP)),(ntohs(client.sin_port)));
		//创建新进程
		pid = fork();
		if(pid < 0) //fork error
		{
			perror("fork error");
			exit(-1);
		}
		else if(pid > 0) //父进程
		{
			//关闭通信文件描述符
			close(cfd);
			//回收子进程
			//注册SIGCHLD信号处理函数
			struct sigaction act;
			act.sa_handler = waitchld;
			sigemptyset(&act.sa_mask);
			act.sa_flags = 0;
			
			sleep(5);

			sigaction(SIGCHLD,&act,NULL);
			
			//解除阻塞
			sigprocmask(SIG_UNBLOCK,&mask,NULL);
		}
		else if(pid == 0) //子进程
		{
			//关闭监听文件描述符
			close(lfd);
			//读数据
			int n ;
			char buf[1024];
			int i;
			while(1)
			{
				n = Read(cfd,buf,sizeof(buf));
				if(n<=0)
				{
					printf("read error or client closed,n == [%d]\n",n);
					break;
				}
				printf("buf=%s\n",buf);
				//发送数据，讲字母转换成大写
				for(i=0;i<n;i++)
				{
					buf[i] = toupper(buf[i]); 
				}
				//发送数据
				Write(cfd,buf,n);
				
			}
			close(cfd);
			//在循环创建子进程时，防止子进程继续创建子进程
			exit(0);
		}

	}

	//关闭监听文件描述符
	close(lfd);
	return 0;
}
